# Create your views here.

from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.contrib.auth import logout
from market.authentification import authentificatePlayer, authentificateUser
from django.template import RequestContext
from market.models import UserProfile, BetRoom, Player, WebSiteComment, Fund
from market.jsonsForCharts import getComments
from market.cometHelper import getChatRoomPlayerStatus
from market.models import DigitalContract, LastVisitedContract, BidOnContract, OfferOnContract, LiquidityToken, TradedContract
from market.chat.betroomBot import sendOnBetroomChat
from market.sendEmailToPlayers import sendEmailToPlayersOfTheBetroom
from market.exceptionHandler import handleRequestPlayerPOSTWithExceptionWhenNotSuspended
from market.exceptionHandler import handleRequestUserPOSTWithException
from market.stupidUserException import StupidUserException
from django.utils import simplejson
from django.core.mail import mail_admins, send_mail
from market import emailSettings
from market.lastVisitedContractHelper import getLastVisitedContract
from datetime import datetime, date, time



###########################################################################################
# implementation of submiting a new contract in the bet room
def submitNewContract0(request, player):

        #----------------- get and check the input
        newContractName = request.POST['newContractName']
        if( " " in newContractName ):
                raise Exception('The name of the contract should not contain any space!')
        betroom = player.dbPlayer.mainStrategy.betroom
        listOfContractsWithThisName = DigitalContract.objects.filter(name__iexact=newContractName, issuer__mainStrategy__betroom=betroom)
        if( len(listOfContractsWithThisName) != 0 ):
                raise StupidUserException('There is already a contract with this name, please chose another name!')

        newContractExpiryDate = request.POST['newContractExpiryDate']#dd/mm/yyyy
        newContractExpiryTime = request.POST['newContractExpiryTime']#hh:mm
        try:
                expiry = datetime.strptime(newContractExpiryDate + ' ' + newContractExpiryTime, "%d/%m/%Y %H:%M")
        except:
                raise StupidUserException('The date or the time has an uncorrect format.')
        if expiry < datetime.now():
                raise StupidUserException('The expiry needs to be in the future.')
        try:
                midPrice = int(request.POST['newContractMidPrice'])
        except:
                raise StupidUserException('The format of the initial mid price is wrong.')
        if midPrice < 5 or midPrice > 95:
                raise StupidUserException('The initial mid price should be in the range 5 - 95.')

        #----------------- create the contract
        newContract = DigitalContract(
                issuer = player.dbPlayer, # Issuer of the contract, allow to acces the bet room in which the the contract can be traded
                name = newContractName,
                description = request.POST['newContractDescription'],#description of what the event related to this contract
                expiry = expiry
                )
        newContract.save()

        #----------------- create the associated liquidity token
        bidOnContract = BidOnContract( contract = newContract, player = player.dbPlayer, price = midPrice - 5 )
        bidOnContract.save()
        offerOnContract = OfferOnContract( contract = newContract, player = player.dbPlayer, price = midPrice + 5 )
        offerOnContract.save()
        liquidityToken = LiquidityToken( offer = offerOnContract, bid = bidOnContract )
        liquidityToken.save()
        
        #-----------------  send an email to all the participants
        mailTxt = newContractName +' is now tradable in '+ betroom.name+'. The event linked to the contract has been decribed by the issuer as:\n'
        mailTxt = mailTxt + newContract.description
        mailTxt = mailTxt + '\n\nConnect to Celibax at http://polux.alwaysdata.net/ to trade this contract!'
        sendEmailToPlayersOfTheBetroom( betroom, 'New Contract in '+ betroom.name+'!', mailTxt )
        return 0

        
# submit a new contract in the bet room
def submitNewContract(request):
        return handleRequestPlayerPOSTWithExceptionWhenNotSuspended(  globals()["submitNewContract0"], request )


###########################################################################################
# implementation of notifying the data base that the player has requested a constrat. Aftr the server aknowledges it, the player will send a data request
def requestSelectedContract0(request, player):

        requestedContract = request.POST['requestedContract']
        listOfContractsWithThisName = DigitalContract.objects.filter(
                name = requestedContract,
                issuer__mainStrategy__betroom = player.dbPlayer.mainStrategy.betroom
                )
        if( len(listOfContractsWithThisName) != 1 ):
                raise StupidUserException('There is no such contract called ' + requestedContract )
        contract = listOfContractsWithThisName[0]
        if contract.expiry < datetime.now():
                contract.settlementStatus = -1
                contract.save()
        lastVisitedContractList = LastVisitedContract.objects.filter( player = player.dbPlayer )
        if len( lastVisitedContractList ) == 0:
                lastVisitedContract = LastVisitedContract( player = player.dbPlayer, contract = contract )
        else:
                lastVisitedContract = lastVisitedContractList[0]
                lastVisitedContract.contract = contract
        lastVisitedContract.save()
        return 0


# notify the data base that the player has requested a constrat. Aftr the server aknowledges it, the player will send a data request
def requestSelectedContract(request):
        return handleRequestPlayerPOSTWithExceptionWhenNotSuspended(  globals()["requestSelectedContract0"], request )
               

###########################################################################################
# raise a StupidUserException if the contract is fixed (settlementStatus == -1) or settled ( settlementStatus>-1)
def checkUnFixedUnSettled( contract ):
        if contract.expiry < datetime.now():
                contract.settlementStatus = -1
                contract.save()
        if contract.settlementStatus > -1:
                raise StupidUserException( 'The contract is already settled and no transaction are not authorised any longer.' )
        if contract.settlementStatus > -2:
                raise StupidUserException( 'The contract has fixed and is waiting for settlement. Transaction are not authorised except if your administrator undo the fixing of this contract.' )


###########################################################################################
# trade the contract
def tradeContract( order, hitBid, counterPart ):

        # check the contract status
        checkUnFixedUnSettled( order.contract )

        # determine the buyer and the seller
        liquidityToken = LiquidityToken.objects.get( offer__contract = order.contract  )
        # The liquidity token should be chosen in priority.
        # Throw if this order has the same price than the associated liquidity token but is not the same object 
        if hitBid:
                seller = counterPart
                buyer = order.player
                if liquidityToken.bid.price == order.price and order != liquidityToken.bid and liquidityToken.holder != counterPart:
                        raise Exception('At equal price, the liquidity token bid should be chosen in priority.' )
        else:
                seller = order.player
                buyer = counterPart
                if liquidityToken.offer.price == order.price and order != liquidityToken.offer and liquidityToken.holder != counterPart:
                        raise Exception('At equal price, the liquidity token offer should be chosen in priority.' )

        # check that the buyer has enough money 
        buyingFund = Fund.objects.get( player = buyer )
        price = order.price
        if price >  buyingFund.numberOfAssetsInFund : 
                raise StupidUserException('The buyer is not reach enough' )

        # transfer the money
        buyingFund.numberOfAssetsInFund = buyingFund.numberOfAssetsInFund - price
        buyingFund.save()
        sellingFund = Fund.objects.get( player = seller )
        sellingFund.numberOfAssetsInFund = sellingFund.numberOfAssetsInFund + price
        sellingFund.save()

        # create a Traded contract
        tradedContract = TradedContract( 
                contract = order.contract,
                seller = seller,
                buyer = buyer,
                tradedPrice = price
                )
        tradedContract.save()
        
        # delete the current liquidity token
        liquidityToken = LiquidityToken.objects.get( offer__contract = order.contract )
        # we want to delete the order, but if it is one off the liquidity token order, it is deleted anyway
        deleteOrder = True
        if hitBid:# we are dealing with a bid
                deleteOrder = not( liquidityToken.bid == order )
                # the counterPart has just sold a contract,
                # he can be forced to unwind making 9 chochoros (he has to buy at the price - 9)
                # and forced to sell another for one chochoros more
                newOfferPrice = min( price + 1, 100 )
                newBidPrice = max( price - 9, 0 )
                msg = seller.userBeingThisPlayer.username+ ' has sold one ' + order.contract.name + ' contract'
        else:# we are dealing with an offer
                deleteOrder = not( liquidityToken.offer == order )
                # the counterPart has just bougt a contract,
                # he can be forced to unwind making 9 chochoros (he has to sell at the price + 9)
                # and forced to buy another for one chochoros less
                newOfferPrice = min( price + 9, 100 )
                newBidPrice = max( price - 1, 0 )
                msg = buyer.userBeingThisPlayer.username+ ' has bought one ' + order.contract.name + ' contract'
        liquidityToken.offer.delete()#this delete also the liquidity token
        liquidityToken.bid.delete()  
                
        # create the new liquidity token
        newBid = BidOnContract( contract = order.contract, player = counterPart, price = newBidPrice )
        newBid.save()
        newOfferOnContract = OfferOnContract( contract = order.contract, player = counterPart, price = newOfferPrice )
        newOfferOnContract.save()
        newLiquidityToken = LiquidityToken( bid = newBid , offer = newOfferOnContract )
        newLiquidityToken.save()
        betroom = buyer.mainStrategy.betroom
        sendOnBetroomChat( betroom, counterPart.userBeingThisPlayer.username, str(msg) )
        # send an email to the player whom order has been exercised
        if order.player.userBeingThisPlayer.get_profile().sendMeEmails:
                email_object = "Action in " + counterPart.mainStrategy.betroom.name
                if hitBid:# we are dealing with a bid
                         email_txt  ="Congratulation, somebody has just hit your bid at "
                else:#
                        email_txt  ="Congratulation, somebody has just lifted your offer at "
                email_txt =  email_txt + str(price) + " on contract " + order.contract.name+"."
                send_mail( email_object, email_txt, emailSettings.SITE_HOST_EMAIL, [ order.player.userBeingThisPlayer.email ], fail_silently=True )

        # delete the order if it was not one of the liquidity token order
        if deleteOrder:
                order.delete()
        
        return

###########################################################################################
# implementation of selling the contract to the higher bid
def hitBid0(request, player):
        contract = getLastVisitedContract( player.dbPlayer )
        bidOnContractList = BidOnContract.objects.filter( contract = contract ).exclude(player = player.dbPlayer).order_by('-price')
        if len( bidOnContractList ) == 0:
                raise StupidUserException('There is currently no bid')
        bid = bidOnContractList[0]

        #At equal price, the liquidity token bid should be chosen in priority.
        liquidityToken = LiquidityToken.objects.get( offer__contract = contract )
        if liquidityToken.bid.price == bid.price and liquidityToken.bid.player != player:
                bid = liquidityToken.bid
        tradeContract( bid, True, player.dbPlayer )
        return 0
        

# sell the contract to the higher bid
def hitBid(request):
        return handleRequestPlayerPOSTWithExceptionWhenNotSuspended(  globals()["hitBid0"], request )

###########################################################################################
# implementation of buying the contract to the lower offer
def liftOffer0(request, player):
        contract = getLastVisitedContract( player.dbPlayer )
        offerOnContractList = OfferOnContract.objects.filter( contract = contract ).exclude(player = player.dbPlayer).order_by('price')
        if len( offerOnContractList ) == 0:
                raise StupidUserException('There is currently no offer' )
        #At equal price, the liquidity token offer should be chosen in priority.
        offer = offerOnContractList[0]
        liquidityToken = LiquidityToken.objects.get( offer__contract = contract )
        if liquidityToken.offer.price == offer.price and liquidityToken.offer.player != player:
                offer = liquidityToken.offer
        tradeContract( offer, False, player.dbPlayer )
        return 0
        

# buy the a contract to the lower offer
def liftOffer(request):
        return handleRequestPlayerPOSTWithExceptionWhenNotSuspended(  globals()["liftOffer0"], request )

###########################################################################################
# implementation of adding a bid to the order book
def addABid0(request, player):
        contract = getLastVisitedContract( player.dbPlayer )
        offerOnContractList = OfferOnContract.objects.filter( contract = contract ).order_by('price')
        maxPrice = 100 
        if len( offerOnContractList ) != 0:
                maxPrice = offerOnContractList[0].price
                
        try:
                newPrice = int(request.POST['newPrice'])
        except:
                newPrice = -1
        print "maxPrice: " +str(maxPrice)
        if newPrice < 1 or newPrice >= maxPrice:
                erroStr = 'The price should be whole number of chochoros between 1 and ' + str(maxPrice-1)
                errorStr = erroStr+ '. Instead of bidding above '+ str(maxPrice-1)+', you may want to excercise an existing offer.'
                raise StupidUserException(errorStr)

        newBidOnContract = BidOnContract( contract = contract, player = player.dbPlayer, price = newPrice )
        newBidOnContract.save()
        playerName = player.dbPlayer.userBeingThisPlayer.username
        sendOnBetroomChat( player.dbPlayer.mainStrategy.betroom, playerName, playerName + ' has a new bid' )
        return 0
        

# add a bid to the order book
def addABid(request):
        return handleRequestPlayerPOSTWithExceptionWhenNotSuspended(  globals()["addABid0"], request )


###########################################################################################
# implementation of adding an offer to the order book
def addAnOffer0(request, player):
        contract = getLastVisitedContract( player.dbPlayer )
        bidOnContractList = BidOnContract.objects.filter( contract = contract ).order_by('-price')
        minPrice = 0
        if len( bidOnContractList ) != 0:
                minPrice = bidOnContractList[0].price
                
        try:
                newPrice = int(request.POST['newPrice'])
        except:
                newPrice = 101
                
        if newPrice > 99 or newPrice <= minPrice:
                erroStr = 'The price should be whole number of chochoros between ' + str(minPrice+1) +' and 99.'
                errorStr = erroStr+ ' Instead of offering below '+str(minPrice+1) +', you may want to excercise an existing bid.'
                raise StupidUserException(errorStr)

        newOfferOnContract = OfferOnContract( contract = contract, player = player.dbPlayer, price = newPrice )
        newOfferOnContract.save()
        playerName = player.dbPlayer.userBeingThisPlayer.username
        sendOnBetroomChat( player.dbPlayer.mainStrategy.betroom, playerName, playerName + ' has a new offer' )
        return 0



# add an offer to the order book
def addAnOffer(request):
        return handleRequestPlayerPOSTWithExceptionWhenNotSuspended(  globals()["addAnOffer0"], request )

###########################################################################################
# implementation specifying the number of bids in the order book
def specifyNumberOfOrder(request, player, doBid):
        contract = getLastVisitedContract( player )
        checkUnFixedUnSettled( contract )
        
        try:
                specifiedPrice = int(request.POST['specifiedPrice'])
        except:
                specifiedPrice = -1
        try:
                specifiedNbOfContract = int(request.POST['specifiedNbOfContract'])
        except:
                raise StupidUserException( 'Please check the number of contracts you have input. It must be a positive (or null) integer.' )

        
        if doBid:
                contractStr = 'bid'
                orderOnContractList = BidOnContract.objects.filter( contract = contract, player = player, price = specifiedPrice )
        else:
                contractStr = 'offer'
                orderOnContractList = OfferOnContract.objects.filter( contract = contract, player = player, price = specifiedPrice )
        currentNumber = len(orderOnContractList)
        # if the specified number is the current number, do nothing
        if currentNumber == specifiedNbOfContract:
                return 0

        # This function should only called when there is already one contract
        if currentNumber == 0:
                raise Exception( player.userBeingThisPlayer.username +  ' has no ' + contractStr +' at price ' + str(specifiedPrice) )
        
        # adding is easy
        if specifiedNbOfContract > currentNumber:
                for i in range(specifiedNbOfContract-currentNumber):
                        if doBid:
                                tmp = BidOnContract( contract = contract, player = player, price = specifiedPrice )
                        else:
                                tmp = OfferOnContract( contract = contract, player = player, price = specifiedPrice )
                        tmp.save()
                return 0
        
        # we now have to deal with specifiedNbOfContract < currentNumber. The player cannot get rid of his liquidity order
        liquidityToken = LiquidityToken.objects.get( offer__contract = contract )
        if specifiedNbOfContract == 0 and len( orderOnContractList ) == 1:
                if orderOnContractList[0] == liquidityToken.bid:
                        raise StupidUserException( 'This bid is linked to the liquidity token and you cannot get rid of it. Cf. help on liquidity token below.' )
                if orderOnContractList[0] == liquidityToken.offer:
                        raise StupidUserException( 'This offer is linked to the liquidity token and you cannot get rid of it. Cf. help on liquidity token below.' )
        # remove the liquidity order before modifying the list
        if doBid:
                orderOnContractList = orderOnContractList.exclude( id = liquidityToken.bid.id )
        else:
                orderOnContractList = orderOnContractList.exclude( id = liquidityToken.offer.id )
        # update the number of contract that we can delete
        potentialyDeletable = len(orderOnContractList)
        specifiedNbOfContract = max(currentNumber - potentialyDeletable, specifiedNbOfContract )#first arg is zero or 1
        # remove the correct number of contracts
        for i in range(currentNumber - specifiedNbOfContract):
                orderOnContractList[len(orderOnContractList)-1 - i].delete()

        return 0


###########################################################################################
# implementation specifying the number of bids in the order book
def specifyNumberOfBids0(request, player):
        playerName = player.dbPlayer.userBeingThisPlayer.username
        sendOnBetroomChat( player.dbPlayer.mainStrategy.betroom, playerName, playerName + ' has updated his number of bids' )
        return specifyNumberOfOrder(request, player.dbPlayer, True )


# specify the number of bids in the order book
def specifyNumberOfBids(request):
        return handleRequestPlayerPOSTWithExceptionWhenNotSuspended(  globals()["specifyNumberOfBids0"], request )


###########################################################################################
# implementation specifying the number of bids in the order book
def specifyNumberOfOffers0(request, player):
        playerName = player.dbPlayer.userBeingThisPlayer.username
        sendOnBetroomChat( player.dbPlayer.mainStrategy.betroom, playerName, playerName + ' has updated his number of offers' )
        return specifyNumberOfOrder(request, player.dbPlayer, False )

# specify the number of bids in the order book
def specifyNumberOfOffers(request):
        return handleRequestPlayerPOSTWithExceptionWhenNotSuspended(  globals()["specifyNumberOfOffers0"], request )

